﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using DesigneSpaceExplorerVEGA.Adapters;
using DesigneSpaceExplorerVEGA.Models;
using GeneticAlgorithm;
using OxyPlot.Series;
using PSatSimCom;
using SelectorVE;

namespace DesigneSpaceExplorerVEGA.Windows
{
    /// <summary>
    /// Interaction logic for FrameWindow.xaml
    /// </summary>
    public partial class FrameWindow : Window
    {
        private ApplicationModel _model;
        private GeneticCycler _algorithm;
        private PSatSim _simulator;
        private VectorEvaluator _selector;
        private About _about;
        private Settings _settings;
        private Thread _algorithmThread;

        private const int DefaultPopulationCount = 12;

        private static readonly int DefaultThreadCount = Environment.ProcessorCount > 1
                                                        ? Environment.ProcessorCount - 1
                                                        : 1;

        private List<SimulatedConfiguration> _configurations = new List<SimulatedConfiguration>();

        public FrameWindow()
        {
            InitializeComponent();
            SetDefaults();
        }

        private void SetDefaults()
        {
            _model = new ApplicationModel();
            _selector = new VectorEvaluator();
            _simulator = new PSatSim();
            _simulator.ThreadCount = DefaultThreadCount;

            if (_algorithmThread != null)
                _algorithmThread.Abort();
            _algorithmThread = null;

            DataContext = _model;

            ParameterTextBox.Text = string.Empty;
            ConfIpcTextBox.Text = string.Empty;
            ConfPowerTextBox.Text = string.Empty;
            
            _algorithm = new GeneticCycler(_simulator, _selector);
            _algorithm.OnClicleDone += _algorithm_OnClicleDone;
            _algorithm.PopulationSize = DefaultPopulationCount;
            _algorithm.Initialize();

            _model.CurrentCycle = 0;
            _model.CycleCount = 3;
            _model.CurrentGeneration = 0;
            _model.Hypervolume = 0;
            _model.ThreadCount = DefaultThreadCount;
            _model.PopulationCount = DefaultPopulationCount;

            SetWindowState(WindowMode.ConfigurationMode);
        }

        private void _algorithm_OnClicleDone(GeneticCycler sender, GeneticCyclerArgs e)
        {
            _configurations.AddRange(e.SimulatedPopulation);
            var hv = new Hypervolume {Input = DspScatterPointAdapter.ToScatterPointList(_configurations)};
            hv.Compute();

            var bestConfIpc = _configurations[0];
            var bestConfPower = _configurations[0];
            foreach (var conf in _configurations)
            {
                bestConfIpc = bestConfIpc.GetScore("ipc") > conf.GetScore("ipc") ? bestConfIpc : conf;
                bestConfPower = bestConfPower.GetScore("power") < conf.GetScore("power") ? bestConfPower : conf;
            }

            Dispatcher.Invoke(() =>
            {
                _model.CurrentCycle = e.CurrentCycle;
                _model.CurrentGeneration = e.Generation;
                _model.UpdateXoYModel(DspScatterPointAdapter.ToScatterPointList(e.SimulatedPopulation),hv.Input, hv.Pareto, hv.ExtremisPoint);
                _model.Hypervolume = hv.Output;
                _model.UpdateHvModel(hv.Output);

                ParameterTextBox.Text = string.Empty;
                ConfIpcTextBox.Text = string.Empty;
                ConfPowerTextBox.Text = string.Empty;

                ParameterTextBox.Text += "Parametrii" + Environment.NewLine;
                ConfIpcTextBox.Text += "Top IPC" + Environment.NewLine;
                ConfPowerTextBox.Text += "Top Consum" + Environment.NewLine;

                foreach (var configurationParameter in PSatSim.ConfigurationParameters)
                {
                    ParameterTextBox.Text += configurationParameter + Environment.NewLine;
                    ConfIpcTextBox.Text += bestConfIpc.GetString(configurationParameter) + Environment.NewLine;
                    ConfPowerTextBox.Text += bestConfPower.GetString(configurationParameter) + Environment.NewLine;
                }

                SetWindowState(WindowMode.IdleMode);
            });
        }

        public void SetWindowState(WindowMode mode)
        {
            switch (mode)
            {
                case WindowMode.ConfigurationMode:
                    StepButton.IsEnabled = true;
                    RunButton.IsEnabled = true;
                    StopButton.IsEnabled = false;
                    ResetButton.IsEnabled = true;

                    RunMenuItem.IsEnabled = true;
                    StepMenuItem.IsEnabled = true;
                    StopMenuItem.IsEnabled = false;
                    ConfigurateMenuItem.IsEnabled = true;

                    StatusProgress.Visibility = Visibility.Hidden;
                    StatusMessage.Content = "Asteptare dupa configuratii";
                    break;
                case WindowMode.RunningMode:
                    StepButton.IsEnabled = false;
                    RunButton.IsEnabled = false;
                    StopButton.IsEnabled = true;
                    ResetButton.IsEnabled = false;

                    RunMenuItem.IsEnabled = false;
                    StepMenuItem.IsEnabled = false;
                    StopMenuItem.IsEnabled = true;
                    ConfigurateMenuItem.IsEnabled = false;

                    StatusProgress.Visibility = Visibility.Visible;
                    StatusMessage.Content = "Rulare simulari";
                    break;
                case WindowMode.IdleMode:
                    StepButton.IsEnabled = true;
                    RunButton.IsEnabled = true;
                    StopButton.IsEnabled = false;
                    ResetButton.IsEnabled = true;

                    RunMenuItem.IsEnabled = true;
                    StepMenuItem.IsEnabled = true;
                    StopMenuItem.IsEnabled = false;
                    ConfigurateMenuItem.IsEnabled = false;

                    StatusProgress.Visibility = Visibility.Hidden;
                    StatusMessage.Content = "Stare de asteptare";
                    break;
                default:
                    throw new ArgumentOutOfRangeException("mode");
            }
            ConsoleTextBox.AppendText("Set window mode: " + mode + Environment.NewLine);
        }

        private void StepHandler(object sender, RoutedEventArgs e)
        {
            _algorithmThread = new Thread(() =>
            {
                Dispatcher.Invoke(() => SetWindowState(WindowMode.RunningMode));
                _algorithm.Step();
            });

            _algorithmThread.Start();
        }

        private void RunHandler(object sender, RoutedEventArgs e)
        {
            _algorithmThread = new Thread(() =>
            {
                Dispatcher.Invoke(() => SetWindowState(WindowMode.RunningMode));
                _algorithm.CycleCount = _model.CycleCount;
                _algorithm.Run();
            });

            _algorithmThread.Start();
        }

        private void StopHandler(object sender, RoutedEventArgs e)
        {
            if (_algorithmThread != null)
            {
                _algorithmThread.Abort();
            }
            SetWindowState(WindowMode.IdleMode);
        }

        private void ConfigureHandler(object sender, RoutedEventArgs e)
        {
            _settings = new Settings(_model);
            _settings.ShowDialog();
        }

        private void OpenAboutHandler(object sender, RoutedEventArgs e)
        {
            _about = new About();
            _about.OkButton.Click += CloseAboutHandler;
            _about.ShowDialog();
        }

        private void CloseAboutHandler(object sender, RoutedEventArgs e)
        {
            _about.Close();
        }

        private void ResetHandler(object sender, RoutedEventArgs e)
        {
            SetDefaults();
        }
    }

    public enum WindowMode
    {
        ConfigurationMode,
        RunningMode,
        IdleMode
    }
}